
#include <string.h>
#include <sstream>
#include <sys/types.h>
#include <sys/time.h>
#include "Debug.h"
#include "Events.h"
#include "define.h" 
using namespace std;

void g_free_FObject(void * fg)
{
        FObject *p = (FObject *)fg;
        if(p!=NULL)
                delete p;
}

void g_free_FGrid(void * fg)
{
	FGrid *p = (FGrid *)fg;
	if(p!=NULL)
		delete p;
}

Events::Events(const char *name, const char *lib, const char *conf, int isize, float fFactor, int kickout)
{
	m_scName = name;
	m_scLib = lib;
	m_scConf = conf;
        m_iGCSize =  isize;
        m_fGCFactor = fFactor;
        m_iKickout = kickout;
        m_iUpdateMax = 1000;
        m_iReturnType = 0;
        m_iReturnMaxCounts = 1000;

        m_pCons = NULL;
        g_static_rw_lock_init(&_Lock);
        m_TB = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free_FGrid);

        g_static_rw_lock_init(&_uLock);
        m_TU = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
}

Events :: ~Events()
{
        DEBUG1("release Events RESOURCE!!!");
	if(m_TB != NULL)
		g_hash_table_destroy(m_TB);
	if(m_TU != NULL)
		g_hash_table_destroy(m_TU);

        if(m_pCons != NULL)
             delete m_pCons;

	g_static_rw_lock_free(&_Lock);
	g_static_rw_lock_free(&_uLock);
}

bool  Events::Initialize()
{
/**
 数据库连接关闭，导致consumer调用失败，从而致使系统重新调用Initialize()，如果多个线程同时执行此函数，就将导致程序崩溃! 
 reload_User_Friends有锁保护，而Initialize只在reload_User_Friends函数中被调用！  
*/
	if(m_pCons != NULL)
	{
		m_pCons->callShutdown();
		delete m_pCons;
	}
	m_pCons = new ConsumerDriver(m_scName.c_str(), m_scLib.c_str() , 1); //RTLD_LAZY);
	if(m_pCons == NULL)
	{
		ERRO << "error to load consumer = "<< m_scLib <<endl;
		return false;
	}
	if(!m_pCons->callInit(m_scName.c_str(), m_scConf.c_str()))
	{
		ERRO << "error to callInit on "<< m_scName <<endl;
		return false;
	}
	INFO<<m_scName<<" callInit successful !"<<endl;
	return true;
}
/*

*/
bool Events::GC(long lt, int size)
{
/*
 调用GC的函数必须加锁！
 */
        GHashTable *rtb = g_hash_table_new(g_str_hash, g_str_equal);
	GHashTableIter iter;
	gpointer keyit, valueit;
        DEBUG2("Kickout = ", m_iKickout);
	g_hash_table_iter_init(&iter, m_TB);
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
		FGrid *f = (FGrid *)valueit;
                if( f!=NULL &&  f->lasttime < (lt - m_iKickout))
			g_hash_table_insert(rtb, keyit, valueit);
	}

/*
 * rtb's size < size, means small users is expire, so turn m_iKickout more small. 
 */
        if(g_hash_table_size(rtb) < (unsigned )size)
		m_iKickout = m_iKickout/2;
	while(true)
	{  
/*
 can't use "while(g_hash_table_iter_next()){ ... }", because mybe one item in rtb will be remove, this will make g_hash_table_iter_next() to fail!
such as:
 rtb's items = {A, B}
 A's frs = {B, C}
 B's frs = {A, C, D}
 C's frs = {A, B}
 I should remove A from B, and remove A from C, and set B's status='n' and C's status='n', and then remove A.
*/ 
		g_hash_table_iter_init(&iter, rtb); // {A, B}
		if(g_hash_table_iter_next(&iter, &keyit, &valueit))
		{
			FGrid *fs = (FGrid *)valueit;
			if(fs == NULL)
				continue ;  
			GHashTableIter iter2;
			gpointer keyit2, valueit2;
/* I want to release user = iter, but I must get his friends list first, 
 * and then I should remove it from his friends's friends list.
 * in then end, I will remove it from m_TB.
 * such as:
 *   iter = A, fs->owner =='A', fs->frs={B, C}
 */
			DEBUG3((char *)keyit, "' friends size = ", g_hash_table_size(fs->frs));
                        // foreach A's frs :: B, C
			g_hash_table_iter_init(&iter2, fs->frs); 
			while(g_hash_table_iter_next(&iter2, &keyit2, &valueit2))
			{
				/*  f2 = B (C) */
				FGrid *f2 = NULL;
				if(keyit2 != NULL)
					f2 = (FGrid *) g_hash_table_lookup(m_TB, keyit2);
				if(f2!= NULL && strcmp(fs->owner, f2->owner) != 0)
				{
                                        DEBUG5("remove ",(char *)keyit, " from ", (char *)keyit2, "'s friends list and set status='n'");
					g_hash_table_remove(f2->frs, keyit);
					f2->status = 'n';
				}
			}
                        DEBUG3("remove ",(char *)keyit," from rtb");
			g_hash_table_remove(rtb, keyit);   
                        DEBUG3("remove ",(char *)keyit," from m_TB");
			g_hash_table_remove(m_TB, keyit);
		}
                else
                      break;
	}	
        g_hash_table_destroy(rtb);
	return true;
}

int Events :: Get_Users_Graph(char *user1, char *user2, char **wbuf, unsigned &wsize, unsigned &wbytes)
{
	int iret = BRUNCE_ERR_SYSTEM;
	g_static_rw_lock_writer_lock(&_Lock); 
        iret =  get_users_graph(user1, user2, wbuf, wsize, wbytes);

        int iSize = g_hash_table_size(m_TB);
        if(iSize > m_iGCSize)
        {
             long lt = time(NULL);
             GC(lt, (int)(m_iGCSize*(1 - m_fGCFactor)));
        }
	g_static_rw_lock_writer_unlock(&_Lock); 
        return iret;
}

/*
定时处理函数，对于超时的用户，系统会放入更新列表中，系统通过定时触发更新这些用户的信息

*/
bool Events::Process_Worklist(long lt)
{
	g_static_rw_lock_writer_lock(&_uLock);
	int iUpdate = g_hash_table_size(m_TU);
        DEBUG3("\t NOW, ", iUpdate, " users need to be update!!!\t");
	g_static_rw_lock_writer_unlock(&_uLock);

	while(iUpdate > 0)
	{
		GHashTableIter iter;
		gpointer keyit, valueit;
		DEBUG2("Kickout = ", m_iKickout);
		g_static_rw_lock_writer_lock(&_Lock);
		g_static_rw_lock_writer_lock(&_uLock);
		g_hash_table_iter_init(&iter, m_TU);
		while (g_hash_table_iter_next(&iter, &keyit, &valueit))
		{
			char *uuser = (char *)keyit;
			FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, uuser);
			if(f1 != NULL)
			{
				DEBUG2(uuser, " is timeout, reload them!");
				reload_User_Friends(uuser, f1, lt);
			}
			g_hash_table_remove(m_TU, uuser);
			break;
		}
		iUpdate--; 

		g_static_rw_lock_writer_unlock(&_uLock);
		g_static_rw_lock_writer_unlock(&_Lock);
		usleep(5);
	}
	return true;
}

int Events :: Get_Friends_Friends(char *user, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // length + retcode + counts + { ulen+ user + nlen + name + type } ... ...
	int iret = BRUNCE_ERR_SYSTEM;
        if(wsize < 1024)
	{
		DEBUG1("realloc(*wbuf, wbytes+1)");
		char *new_wbuf = (char *)g_realloc(*wbuf, 1024);
		if(new_wbuf == NULL){
			ERRO<<"can't malloc for write buffer !"<<endl;
			wbytes = 0;
			return iret;
		}
                *wbuf = new_wbuf;
		wsize = 1024;
	}
        long lt = time(NULL);
        iret = prepare_User_Friends(user, lt);
        if(iret < BRUNCE_SUCCESS)
               return iret;

	g_static_rw_lock_reader_lock(&_Lock); 
	int iSize = g_hash_table_size(m_TB);
	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user);
	if(f1 != NULL)
		iret =  get_friends_friends(f1, wbuf, wsize, wbytes);
	else
		iret = BRUNCE_ERR_NO_DATA;
	g_static_rw_lock_reader_unlock(&_Lock); 

	if(iSize > m_iGCSize)
	{
		g_static_rw_lock_writer_lock(&_Lock); 
		GC(lt, (int)(m_iGCSize*(1 - m_fGCFactor)));
		g_static_rw_lock_writer_unlock(&_Lock); 
	}
	return iret;
}

int Events :: Get_User_Friends(char *user, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // length + retcode + counts + { ulen+ user + nlen + name + type } ... ...
	int iret = BRUNCE_ERR_SYSTEM;
        if(wsize < 1024)
	{
		DEBUG1("realloc(*wbuf, wbytes+1)");
		char *new_wbuf = (char *)g_realloc(*wbuf, 1024);
		if(new_wbuf == NULL){
			ERRO<<"can't malloc for write buffer !"<<endl;
			wbytes = 0;
			return iret;
		}
                *wbuf = new_wbuf;
		wsize = 1024;
	}
         long lt = time(NULL);
	 iret = prepare_User_Friends(user, lt);
        if(iret < BRUNCE_SUCCESS)
               return iret;

 	g_static_rw_lock_reader_lock(&_Lock); 
        int iSize = g_hash_table_size(m_TB);

	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user);
	if(f1 != NULL)
		iret =  get_users_friends(f1, wbuf, wsize, wbytes);
	else
		iret = BRUNCE_ERR_NO_DATA;
	g_static_rw_lock_reader_unlock(&_Lock); 

	if(iSize > m_iGCSize)
	{
		g_static_rw_lock_writer_lock(&_Lock); 
		GC(lt, (int)(m_iGCSize*(1 - m_fGCFactor)));
		g_static_rw_lock_writer_unlock(&_Lock); 
	}
	return iret;
}

int Events :: Get_User_Friends(char *user, unsigned type, unsigned start, unsigned num, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // length + retcode + total + counts + { ulen+ user + nlen + name } ... ...
	int iret = BRUNCE_ERR_SYSTEM;
        if(wsize < 1024)
	{
		DEBUG1("realloc(*wbuf, wbytes+1)");
		char *new_wbuf = (char *)g_realloc(*wbuf, 1024);
		if(new_wbuf == NULL){
			ERRO<<"can't malloc for write buffer !"<<endl;
			wbytes = 0;
			return iret;
		}
                *wbuf = new_wbuf;
		wsize = 1024;
	}
         long lt = time(NULL);
	 iret = prepare_User_Friends(user, lt);
        if(iret < BRUNCE_SUCCESS)
               return iret;

	
 	g_static_rw_lock_reader_lock(&_Lock); 
	int iSize = g_hash_table_size(m_TB);
	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user);
	if(f1 != NULL)
		iret =  get_users_friends(f1, type, start, num, wbuf, wsize, wbytes);
	else
		iret = BRUNCE_ERR_NO_DATA;
	g_static_rw_lock_reader_unlock(&_Lock); 

	if(iSize > m_iGCSize)
	{
		g_static_rw_lock_writer_lock(&_Lock); 
		GC(lt, (int)(m_iGCSize*(1 - m_fGCFactor)));
		g_static_rw_lock_writer_unlock(&_Lock); 
	}
	return iret;
}


int Events :: Get_User_Friends(char *user, unsigned start, unsigned num, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // length + retcode + all_counts + argc + { ulen+ user + nlen + name + type } ... ...
	int iret = BRUNCE_ERR_SYSTEM;
        if(wsize < 1024)
	{
		DEBUG1("realloc(*wbuf, wbytes+1)");
		char *new_wbuf = (char *)g_realloc(*wbuf, 1024);
		if(new_wbuf == NULL){
			ERRO<<"can't malloc for write buffer !"<<endl;
			wbytes = 0;
			return iret;
		}
                *wbuf = new_wbuf;
		wsize = 1024;
	}
        long lt = time(NULL);
	iret = prepare_User_Friends(user, lt);
	if(iret < BRUNCE_SUCCESS)
		return iret;

	g_static_rw_lock_reader_lock(&_Lock); 
	int iSize = g_hash_table_size(m_TB);
	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user);
	if(f1 != NULL)
		iret =  get_users_friends(f1, start, num, wbuf, wsize, wbytes);
	else
		iret = BRUNCE_ERR_NO_DATA;
	g_static_rw_lock_reader_unlock(&_Lock); 

	if(iSize > m_iGCSize)
	{
		g_static_rw_lock_writer_lock(&_Lock); 
		GC(lt, (int)(m_iGCSize*(1 - m_fGCFactor)));
		g_static_rw_lock_writer_unlock(&_Lock); 
	}
	return iret;
}

int Events :: Get_Users_Value(char *data, unsigned dlen, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ 
//input =  counts + ulen + user + ... ...
// length + retcode + counts + { ulen+ user + nlen + name + type } ... ...
	int iret = BRUNCE_ERR_SYSTEM;
        if(wsize < 1024)
	{
		DEBUG1("realloc(*wbuf, wbytes+1)");
		char *new_wbuf = (char *)g_realloc(*wbuf, 1024);
		if(new_wbuf == NULL){
			ERRO<<"can't malloc for write buffer !"<<endl;
			wbytes = 0;
			return iret;
		}
                *wbuf = new_wbuf;
		wsize = 1024;
	}
        GHashTable *th = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);

	unsigned ulen = 0;
	unsigned uint_len = sizeof(unsigned);
	unsigned used = 0;
	int icount = 0;
	bcopy(data, &icount, uint_len);
	DEBUG2("user counts = ", icount);
	used = uint_len;
	for(int i=0; i< icount; i++)
	{
		if(dlen < (used + uint_len + 1))
		{
			DEBUG4("length error: dlen=",dlen,"; used = ", used);
			iret = BRUNCE_ERR_ARGV;
			break;
		}
		bcopy(data + used , &ulen,  uint_len);
		used += uint_len;
		if(dlen < (used + ulen))
		{
			iret = BRUNCE_ERR_ARGV;
			DEBUG4("length error: ulen=", ulen,"; used = ", used);
			break;
		}
		char *user = g_strndup((char *)data + used, ulen);
		DEBUG2("reset user=", user);
                g_hash_table_insert(th, user, NULL); 
		used += ulen;
	}
        if(g_hash_table_size(th) <= 0)
        {
              g_hash_table_destroy(th);
              return BRUNCE_ERR_ARGV;
        }
       
	GHashTableIter iter;
	gpointer keyit, valueit;
	g_hash_table_iter_init(&iter, th);
        icount = 0;
	g_static_rw_lock_writer_lock(&_Lock);
        used = 3*uint_len;  // length + retcode + counts 
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
		FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, keyit);
		if(f1 == NULL)
		{
			DEBUG2(keyit," is not exist, create and reload all!");
			char *fuser = (char *)g_strdup((char *)keyit);
                        long lt = time(NULL);
			f1 = new FGrid(fuser, lt);
			g_hash_table_insert(m_TB, fuser, f1);
			bool br = reload_User_Friends(fuser, f1, lt);          
			if(!br && g_hash_table_size(f1->frs) == 0)
			{
				g_hash_table_remove(m_TB, keyit);
				f1 = NULL;
			}     
		}
                if(f1 != NULL)
		{

			ulen = strlen((char *)keyit);
			int vlen = f1->value.length();
			if(wsize < (used + ulen+ vlen + 3*uint_len ))
			{
				DEBUG1("realloc(*wbuf, wbytes+1)");
				char *new_wbuf = (char *)g_realloc(*wbuf, wsize + 2*(used + ulen + vlen + 3*uint_len ));
				if(new_wbuf == NULL){
					ERRO<<"can't malloc for write buffer !"<<endl;
					wbytes = 0;
					iret = BRUNCE_ERR_SYSTEM;
                                        break;
				}
				wsize = wsize + 2*(used + ulen + vlen + 3*uint_len );
                                *wbuf = new_wbuf;
			}

			bcopy(&ulen, *wbuf + used, uint_len);
			used += uint_len;
			bcopy(keyit, *wbuf + used, ulen);
			used += ulen;
			// vlen + value
			bcopy(&vlen, *wbuf + used, uint_len);
			used += uint_len;
			bcopy(f1->value.c_str(), *wbuf + used, vlen);
			used += vlen;
			// type
			int itp = (int)(f1->type);
			bcopy(&itp, *wbuf + used, uint_len);
			used += uint_len;
			icount ++;
                        if(icount > m_iReturnMaxCounts)
                            break;  
		}
         }                
        int iSize = g_hash_table_size(m_TB);
 	g_static_rw_lock_writer_unlock(&_Lock); 
        g_hash_table_destroy(th);

        if(icount > 0)
             iret = BRUNCE_SUCCESS;
        wbytes = used;
	bcopy(&wbytes, *wbuf, uint_len);
	bcopy(&iret, *wbuf + uint_len, uint_len);
	bcopy(&icount, *wbuf + 2*uint_len, uint_len);
	if(iSize > m_iGCSize)
	{
		g_static_rw_lock_writer_lock(&_Lock); 
		GC(time(NULL), (int)(m_iGCSize*(1 - m_fGCFactor)));
		g_static_rw_lock_writer_unlock(&_Lock); 
	}
	return iret;
}



int Events::Reload_User(const char *user)
{ //assert(name != NULL);
	g_static_rw_lock_writer_lock(&_Lock); 
	FGrid *fg = (FGrid*)g_hash_table_lookup(m_TB, user);
        long lt = time(NULL);
	if(fg != NULL)  // if user is exist
            reload_User_Friends((char *)user, fg, lt);
	else //means user is not exist in m_TB, so create it and it's friends
	{
		char *name = g_strdup(user);
		fg = new FGrid(name, lt);
		g_hash_table_insert(m_TB, name, fg);

		bool br = reload_User_Friends((char *)user, fg, lt);
		if( !br && g_hash_table_size(fg->frs) == 0)
		{
			g_hash_table_remove(m_TB, user);
			g_static_rw_lock_writer_unlock(&_Lock); 
			return BRUNCE_ERR_NO_DATA;
		}
	} 
	g_static_rw_lock_writer_unlock(&_Lock); 
	return BRUNCE_SUCCESS;
}


int Events:: Get_Status(char **wbuf, unsigned &wsize, unsigned &wbytes )
{ //assert(name != NULL);
	g_static_rw_lock_reader_lock(&_Lock); 
        int isize = g_hash_table_size(m_TB);
	g_static_rw_lock_reader_unlock(&_Lock); 
        // length + retcode + size + kickout + gcSize + gcFactor
        int uint_len = sizeof(unsigned);
        int used = 2*uint_len;
	bcopy(&isize, *wbuf + used, uint_len);     
	used += uint_len;                         
	bcopy(&m_iKickout, *wbuf + used, uint_len);     
	used += uint_len;
	bcopy(&m_iGCSize, *wbuf + used, uint_len);     
	used += uint_len;
        int iret = BRUNCE_SUCCESS;
        wbytes = used;
	bcopy(&wbytes, *wbuf, uint_len);     
	bcopy(&iret, *wbuf + uint_len, uint_len);     
	
	return BRUNCE_SUCCESS;
}

bool Events::Clear()
{
	return true;
}

///////////////////////////////////////////////////////
int Events :: prepare_User_Friends(char *user, long lt)
{ 
	g_static_rw_lock_writer_lock(&_Lock); 
	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user);
	if(f1 == NULL)
	{
		DEBUG2(user," is not exist, create and reload all!");
		char *fuser = g_strdup(user);
		f1 = new FGrid(fuser, lt);
		g_hash_table_insert(m_TB, fuser, f1);
		bool br = reload_User_Friends(fuser, f1, lt);          
                if(!br && g_hash_table_size(f1->frs) == 0)
		{
			g_hash_table_remove(m_TB, user);
			g_static_rw_lock_writer_unlock(&_Lock); 
			return BRUNCE_ERR_NO_DATA;
		}     
	}
	else
        {
DEBUG6("lasttime = ", f1->lasttime,"; kickout =", m_iKickout, "; lt-kickout = ", lt - m_iKickout);
		if(f1->status != 'y'){
			DEBUG2(user, "'s friends are not all in frs! reload them!");
			reload_User_Friends(user, f1, lt);
		}
		else if(f1->lasttime < (lt - m_iKickout))
		{
                        DEBUG2(f1->owner, " is timeout, so reload it");
			g_static_rw_lock_writer_lock(&_uLock);
			if((int) g_hash_table_size(m_TU) < m_iUpdateMax) 
				g_hash_table_insert(m_TU, g_strdup(f1->owner), NULL);
			g_static_rw_lock_writer_unlock(&_uLock);
		}
	}

 	g_static_rw_lock_writer_unlock(&_Lock); 
	return BRUNCE_SUCCESS;
}



bool Events :: reload_User_Friends(char *user, FGrid *fs, long lt)
{
       GHashTable *friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free_FObject);

	bool bok = m_pCons->callFriends(user, friends);
	if(!bok) // may db connection is lost!
	{
		Initialize();
		bok = m_pCons->callFriends(user, friends);
	}
	if(bok)
	{
		fs->status = 'y';
		if(g_hash_table_size(friends) > 0){
			GHashTableIter iter;
			gpointer keyit, valueit;
			g_hash_table_iter_init(&iter, friends);
			while (g_hash_table_iter_next(&iter, &keyit, &valueit))
			{ 
				FObject *fo = (FObject *)valueit;
				if(fo != NULL)
				{
					FGrid *ff = (FGrid *)g_hash_table_lookup(m_TB, (char *)keyit);
					if(ff != NULL){ // means user is exist now!
						DEBUG3((char *)keyit," is exist, set type = ",(int)fo->type);
						ff->lasttime = lt;
                                                ff->value = fo->value;
                                                ff->type = fo->type;
						if(strcmp(user, (char *)keyit) != 0)
							g_hash_table_insert(fs->frs, ff->owner, ff->owner); 
					}
					else
					{ //user is not exist, so create it! but status = 'n'
						DEBUG3((char *)keyit, " is not exist, type = ",(int)(fo->type));
                                                char *fu = g_strdup((char *)keyit);  
						ff = new FGrid(fu, lt);
						ff->value = fo->value;
						ff->type = fo->type;
						g_hash_table_insert(m_TB, fu, ff);
						if(strcmp(user, (char *)keyit) != 0)
							g_hash_table_insert(fs->frs, ff->owner, ff->owner); 
					}
					DEBUG6("add ", ff->owner, " to ", user," ; size=", g_hash_table_size(fs->frs));
				}
			}
		}
                g_hash_table_destroy(friends);
		return true;
	}
        g_hash_table_destroy(friends);
	return false;
}

int Events :: get_users_graph(char *user1, char *user2, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // length + retcode + depth + items + {item1+item2+item3}: item1=vlen+val+....
/* 此函数已经在外部被锁同步,内部不能有锁！ */

	int iret = BRUNCE_ERR_SYSTEM;
	if(wsize < 1024)
	{
		DEBUG1("realloc(*wbuf, wbytes+1)");
		char *new_wbuf = (char *)g_realloc(*wbuf, 1024);
		if(new_wbuf == NULL){
			ERRO<<"can't malloc for write buffer !"<<endl;
			wbytes = 0;
			return iret;
		}
                *wbuf = new_wbuf;
		wsize = 1024;
	}
        unsigned ulen = 0;
        unsigned uint_len = sizeof(unsigned);
        unsigned used = 0;
	long lt = time(NULL);
	FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, user1);
	if(f1 == NULL)
	{
/*
 * if user is not exist, then create it and reload friends from db
 */
                DEBUG2(user1, " is not exist in table, reload it and his firends!");
		char *user = g_strdup(user1);
		f1 = new FGrid(user, lt);
		g_hash_table_insert(m_TB, user, f1);
		bool br = reload_User_Friends(user, f1, lt);  
                if( !br && g_hash_table_size(f1->frs) == 0)
                {
                    g_hash_table_remove(m_TB, user1);
                    return BRUNCE_ERR_NO_DATA;
                }     
	} 
	else
        {
DEBUG6("lasttime = ", f1->lasttime,"; kickout =", m_iKickout, "; lt-kickout = ", lt - m_iKickout);
		if(f1->status != 'y')
		{ 
			DEBUG2(user1, "'s friends are not all in frs! reload them!");
			reload_User_Friends(user1, f1, lt);           
		}
		else if(f1->lasttime < (lt - m_iKickout)) 
		{
                        DEBUG2(f1->owner,  " is timeout, reload it!");
			g_static_rw_lock_writer_lock(&_uLock);
			if( (int)g_hash_table_size(m_TU) < m_iUpdateMax) 
				g_hash_table_insert(m_TU, g_strdup(f1->owner), NULL);
			g_static_rw_lock_writer_unlock(&_uLock);
		}
	}
/*
 * user's friends are not all in frs! 
 */
	int idepth = 0;       
	if(g_hash_table_lookup(f1->frs, user2) != NULL)
	{	
                DEBUG1("Yes, find it!");
	//	INFO<<user1<<"->"<<user2<<endl;
                wbytes = 3*uint_len;
                iret = BRUNCE_SUCCESS;
		bcopy(&wbytes, *wbuf, uint_len);
		bcopy(&iret, *wbuf + uint_len, uint_len);
		bcopy(&idepth, *wbuf + uint_len*2, uint_len);
		return BRUNCE_SUCCESS;
	}

	FGrid *f2 = (FGrid *)g_hash_table_lookup(m_TB, user2);
	if(f2 == NULL)
	{
                DEBUG2(user2, " is not exist in table, reload it and his firends!");
		char *user = g_strdup(user2);
		f2 = new FGrid(user, lt);
		g_hash_table_insert(m_TB, user, f2);
		bool br = reload_User_Friends(user, f2, lt);          
                if(!br && g_hash_table_size(f2->frs) == 0)
                {
                    g_hash_table_remove(m_TB, user2);
                    return BRUNCE_ERR_NO_DATA;
                }     
	} 
	else
        {
       DEBUG6( "lasttime = ", f2->lasttime,"; kickout =", m_iKickout, "; lt-kickout = ", lt - m_iKickout);
		if(f2->status != 'y'){
			DEBUG2(user2, "'s friends are not all in frs! reload them!");
			reload_User_Friends(user2, f2, lt);   
		}
		else if(f2->lasttime < (lt - m_iKickout)) 
		{
			DEBUG2(f2->owner, " is timeout, reload it");
			g_static_rw_lock_writer_lock(&_uLock);
			if((int) g_hash_table_size(m_TU) < m_iUpdateMax) 
				g_hash_table_insert(m_TU, g_strdup(f2->owner), NULL);
			g_static_rw_lock_writer_unlock(&_uLock);
		}
	}

	int icounts = 0;
        idepth = 1;
        char direct = 'a'; 
	GHashTable *h1 = f1->frs;
	GHashTable *h2 = f2->frs;
	if(g_hash_table_size(h1) > g_hash_table_size(h2))
	{
		h1 = f2->frs;
		h2 = f1->frs;
                direct = 'b';
	}
	GHashTableIter iter;
	gpointer keyit, valueit;
	g_hash_table_iter_init(&iter, h1);
        used = 4*uint_len ; //length+retcode+idepth +icounts + ...
//     	bcopy(&direct, *wbuf + used, 1);
//      used += 1;
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
		if(g_hash_table_lookup(h2, keyit) != NULL)
		{
			ulen = strlen((char *)keyit);
			if(wsize < used + ulen + uint_len)
			{
				DEBUG1("realloc(*wbuf, wbytes+1)");
				char *new_wbuf = (char *)g_realloc(*wbuf, wsize*2);
				if(new_wbuf == NULL){
					ERRO<<"can't malloc for write buffer !"<<endl;
					wbytes = 0;
					return  BRUNCE_ERR_SYSTEM;
				}
                                *wbuf = new_wbuf;
				wsize = 2*wsize;
			}
			bcopy(&ulen, *wbuf + used, uint_len);
                        used += uint_len;
			bcopy(keyit, *wbuf + used, ulen);
                        used += ulen;
			DEBUG7(icounts," : ", user1,"->",(char *)keyit,"->",user2);
                        icounts++;
                        if(used >= MAX_WRITE_BUFFER_LEN)
                            break;
                        if(icounts >= m_iReturnMaxCounts)
                            break; 
		}
	}
	if(icounts > 0)
	{
                iret = BRUNCE_SUCCESS;
		wbytes = used;
		bcopy(&wbytes, *wbuf , uint_len);
		bcopy(&iret,  *wbuf + uint_len, uint_len);
                DEBUG6("length=", used,"; set idepth=", idepth, "; offset = ", 2*uint_len);
		bcopy(&idepth, *wbuf + 2*uint_len , uint_len);
		bcopy(&icounts, *wbuf + 3*uint_len, uint_len);
                return iret;
	}
        idepth = 2;
	g_hash_table_iter_init(&iter, h1);
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
        	FGrid *fg2 = (FGrid *)g_hash_table_lookup(m_TB, keyit);
		if(fg2 == NULL)
			continue; 
DEBUG6("lasttime = ", f2->lasttime,"; kickout =", m_iKickout, "; lt-kickout = ", lt - m_iKickout);
                if(f2->status != 'y'){
                        DEBUG2(f2->owner , "'s friends are not all in frs! reload them!");
                        reload_User_Friends(f2->owner, f2, lt);
                }
                else if(f2->lasttime < (lt - m_iKickout))
                {
                        DEBUG2(f2->owner, " is timeout, so reload it");
                        g_static_rw_lock_writer_lock(&_uLock);
			if((int) g_hash_table_size(m_TU) < m_iUpdateMax) 
				g_hash_table_insert(m_TU, g_strdup(f2->owner), NULL);
			g_static_rw_lock_writer_unlock(&_uLock);
                }

		GHashTableIter iter2;
		gpointer keyit2, valueit2;
                DEBUG4("user = ", (char *)keyit, "; frs size =", g_hash_table_size(fg2->frs)); 
		g_hash_table_iter_init(&iter2, fg2->frs);
		while (g_hash_table_iter_next(&iter2, &keyit2, &valueit2))
		{
			if(g_hash_table_lookup(h2, keyit2) != NULL)
			{
				ulen = strlen((char *)keyit);
                                int ulen2 = strlen((char *)keyit2);
				if(wsize < used + ulen + uint_len*2 + ulen2)
				{
					DEBUG1("realloc(*wbuf, wbytes+1)");
					char *new_wbuf = (char *)g_realloc(*wbuf,  2*wsize);
					if(new_wbuf == NULL){
						ERRO<<"can't malloc for write buffer !"<<endl;
						wbytes = 0;
						return  BRUNCE_ERR_SYSTEM;
					}
                                        *wbuf = new_wbuf;
					wsize = 2*wsize;
				}
				if(direct == 'a'){
					bcopy(&ulen, *wbuf + used, uint_len);
					used += uint_len;
					bcopy(keyit, *wbuf + used, ulen);
					used += ulen;

					bcopy(&ulen2, *wbuf + used, uint_len);
					used += uint_len;
					bcopy(keyit2, *wbuf + used, ulen2);
					used += ulen2;
				}
				else{
					bcopy(&ulen2, *wbuf + used, uint_len);
					used += uint_len;
					bcopy(keyit2, *wbuf + used, ulen2);
					used += ulen2;

					bcopy(&ulen, *wbuf + used, uint_len);
					used += uint_len;
					bcopy(keyit, *wbuf + used, ulen);
					used += ulen;
				}

				DEBUG7(icounts," : ", user1,"->",(char *)keyit,"->",user2);
				icounts++;
				if(used >= MAX_WRITE_BUFFER_LEN)
					break;
				if(icounts >= m_iReturnMaxCounts)
					break;
			}
		}
        }

	if(icounts > 0)
	{
                iret = BRUNCE_SUCCESS;
		wbytes = used;
		bcopy(&wbytes, *wbuf , uint_len);
		bcopy(&iret,  *wbuf + uint_len, uint_len);
                DEBUG6("length=", used,"; set idepth=", idepth, "; offset = ", 2*uint_len);
		bcopy(&idepth, *wbuf + 2*uint_len , uint_len);
		bcopy(&icounts, *wbuf + 3*uint_len, uint_len);
                return iret;
	}

        DEBUG4("icounts = ", icounts,"; retcode = ", iret);
        return iret;
}

int Events :: get_users_friends(FGrid *fs, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // length + retcode + counts + {item1+item2+item3} ....
  // item = ulen + user + nlen + value + type
	unsigned ulen = 0;
	unsigned uint_len = sizeof(unsigned);
	unsigned used = 0;
        int iret = BRUNCE_ERR_SYSTEM;
	int icounts = 0; //g_hash_table_size(fs->frs);
        DEBUG2("friends size = ", icounts);
	GHashTableIter iter;
	gpointer keyit, valueit;
        used = 3*uint_len ; //length+retcode+ +icounts + ...
	g_hash_table_iter_init(&iter, fs->frs);
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
		ulen = strlen((char *)keyit);
                FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, keyit);
                int vlen = f1->value.length();    
		if(wsize < (used + ulen+ vlen + 3*uint_len ))
		{
			DEBUG1("realloc(*wbuf, wbytes+1)");
			char *new_wbuf = (char *)g_realloc(*wbuf, wsize + 2*(used + ulen + vlen + 3*uint_len));
			if(new_wbuf == NULL){
				ERRO<<"can't malloc for write buffer !"<<endl;
				wbytes = 0;
				return BRUNCE_ERR_SYSTEM;
			}
                        *wbuf = new_wbuf;
			wsize = wsize + 2*(used + ulen + vlen + 3*uint_len );
		}
                // ulen + user
		bcopy(&ulen, *wbuf + used, uint_len);     
		used += uint_len;                         
		bcopy(keyit, *wbuf + used, ulen);     
		used += ulen;
                // vlen + value
		bcopy(&vlen, *wbuf + used, uint_len);     
		used += uint_len;                         
		bcopy(f1->value.c_str(), *wbuf + used, vlen);     
		used += vlen;
                // type

                int itp = (int)(f1->type);
                if(m_iReturnType > 0)
                    itp =  g_hash_table_size(f1->frs);     
                DEBUG6("name = ", (char *)keyit,"; m_iReturnType = ", m_iReturnType,"; return value =",  itp);
		bcopy(&itp, *wbuf + used, uint_len);     
		used += uint_len;
                icounts++;
		if(used >= MAX_WRITE_BUFFER_LEN)
			break;
		if(icounts >= m_iReturnMaxCounts)
			break;
	}
	wbytes = used;
	iret = BRUNCE_SUCCESS;
	DEBUG4("wbytes=", wbytes, "; iret = ", iret);
	bcopy(&wbytes, *wbuf, uint_len);
	bcopy(&iret, *wbuf + uint_len, uint_len);
	bcopy(&icounts, *wbuf + uint_len*2, uint_len);
	return iret;
}

int Events :: get_users_friends(FGrid *fs, unsigned type, unsigned start, unsigned num, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // length + retcode + total + counts + {item1+item2+item3} ....
  // item = ulen + user + nlen + value 
	unsigned ulen = 0;
	unsigned uint_len = sizeof(unsigned);
	unsigned used = 0;
        int iret = BRUNCE_ERR_SYSTEM;
	unsigned icounts = 0; //g_hash_table_size(fs->frs);
        unsigned itotal = 0;
      
        DEBUG2("friends size = ", icounts);
	GHashTableIter iter;
	gpointer keyit, valueit;
        used = 4*uint_len ; //length+retcode+ + total + icounts + ...
	g_hash_table_iter_init(&iter, fs->frs);
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{

                FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, keyit);
                if((unsigned) (f1->type) != type)
                        continue;
                itotal++;
                if(itotal < start)
                     continue;                
                if(icounts > num || icounts > (unsigned) m_iReturnMaxCounts)
                     continue;
		if(used >= MAX_WRITE_BUFFER_LEN)
			continue;
                 
		ulen = strlen((char *)keyit);
		int vlen = f1->value.length();    
		if(wsize < (used + ulen+ vlen + 2*uint_len ))
		{
			DEBUG1("realloc(*wbuf, wbytes+1)");
			char *new_wbuf = (char *)g_realloc(*wbuf, wsize + 2*(used + ulen + vlen + 2*uint_len ));
			if(new_wbuf == NULL){
				ERRO<<"can't malloc for write buffer !"<<endl;
				wbytes = 0;
				return BRUNCE_ERR_SYSTEM;
			}
			*wbuf = new_wbuf;
			wsize = wsize + 2*(used + ulen + vlen + 2*uint_len );
		}
                // ulen + user
		bcopy(&ulen, *wbuf + used, uint_len);     
		used += uint_len;                         
		bcopy(keyit, *wbuf + used, ulen);     
		used += ulen;
                // vlen + value
		bcopy(&vlen, *wbuf + used, uint_len);     
		used += uint_len;                         
		bcopy(f1->value.c_str(), *wbuf + used, vlen);     
		used += vlen;
                icounts++;
	}
	wbytes = used;
	iret = BRUNCE_SUCCESS;
	DEBUG4("wbytes=", wbytes, "; iret = ", iret);
	bcopy(&wbytes, *wbuf, uint_len);
	bcopy(&iret, *wbuf + uint_len, uint_len);
	bcopy(&itotal, *wbuf + uint_len*2, uint_len);
	bcopy(&icounts, *wbuf + uint_len*3, uint_len);
	return iret;
}


int Events :: get_users_friends(FGrid *fs, unsigned start, unsigned num, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // length + retcode + all_counts + argc + {item1+item2+item3} ....
  // item = ulen + user + nlen + value + type
	unsigned ulen = 0;
	unsigned uint_len = sizeof(unsigned);
	unsigned used = 0;
        int iret = BRUNCE_ERR_SYSTEM;
	int icounts =g_hash_table_size(fs->frs);
        DEBUG2("friends size = ", icounts);
	GHashTableIter iter;
	gpointer keyit, valueit;
        used = 4*uint_len ; //length+retcode+ +all_counts + argc ...
	g_hash_table_iter_init(&iter, fs->frs);
        unsigned iargc = 0;
        if((int)num > m_iReturnMaxCounts)
           num = (unsigned) m_iReturnMaxCounts;
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
                if(iargc < start)
                      continue;
                if(iargc - start >= num)
                      break;
		ulen = strlen((char *)keyit);
                FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, keyit);
                int vlen = f1->value.length();    
		if(wsize < (used + ulen+ vlen + 3*uint_len ))
		{
			DEBUG1("realloc(*wbuf, wbytes+1)");
			char *new_wbuf = (char *)g_realloc(*wbuf, wsize + 2*(used + ulen + vlen + 3*uint_len));
			if(new_wbuf == NULL){
				ERRO<<"can't malloc for write buffer !"<<endl;
				wbytes = 0;
				return BRUNCE_ERR_SYSTEM;
			}
                        *wbuf = new_wbuf;
			wsize = wsize + 2*(used + ulen + vlen + 3*uint_len );
		}
                // ulen + user
		bcopy(&ulen, *wbuf + used, uint_len);     
		used += uint_len;                         
		bcopy(keyit, *wbuf + used, ulen);     
		used += ulen;
                // vlen + value
		bcopy(&vlen, *wbuf + used, uint_len);     
		used += uint_len;                         
		bcopy(f1->value.c_str(), *wbuf + used, vlen);     
		used += vlen;
                // type

                int itp = (int)(f1->type);
                if(m_iReturnType > 0)
                    itp =  g_hash_table_size(f1->frs);     
                DEBUG6("name = ", (char *)keyit,"; m_iReturnType = ", m_iReturnType,"; return value =",  itp);
		bcopy(&itp, *wbuf + used, uint_len);     
		used += uint_len;
		if(used >= MAX_WRITE_BUFFER_LEN)
			break;
                iargc++;
	}
	wbytes = used;
	iret = BRUNCE_SUCCESS;
        iargc = iargc - start;
	DEBUG8("wbytes=", wbytes, "; iret = ", iret, ", counts=", icounts, ", num=", iargc);
	bcopy(&wbytes, *wbuf, uint_len);
	bcopy(&iret, *wbuf + uint_len, uint_len);
	bcopy(&icounts, *wbuf + uint_len*2, uint_len);
	bcopy(&iargc, *wbuf + uint_len*3, uint_len);
	return iret;
}

int Events :: get_friends_friends(FGrid *fs, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // length + retcode + counts + {item1+item2+item3} ....
  // item = ulen + user + nlen + value + type + counts + { subitem1 + ....}
  // subitem = ulen + user + nlen + value + type
	unsigned ulen = 0;
	unsigned uint_len = sizeof(unsigned);
	unsigned used = 0;
        int iret = BRUNCE_ERR_SYSTEM;
	int icounts =g_hash_table_size(fs->frs);
        DEBUG2("friends size = ", icounts);
	GHashTableIter iter;
	gpointer keyit, valueit;
        used = 3*uint_len ; //length+retcode+ +icounts + ...
	g_hash_table_iter_init(&iter, fs->frs);
	while (g_hash_table_iter_next(&iter, &keyit, &valueit))
	{
		ulen = strlen((char *)keyit);
                FGrid *f1 = (FGrid *)g_hash_table_lookup(m_TB, keyit);
                int vlen = f1->value.length();    
		if(wsize < (used + ulen+ vlen + 3*uint_len ))
		{
			DEBUG1("realloc(*wbuf, wbytes+1)");
			char *new_wbuf = (char *)g_realloc(*wbuf, wsize + 2*(used + ulen + vlen + 3*uint_len));
			if(new_wbuf == NULL){
				ERRO<<"can't malloc for write buffer !"<<endl;
				wbytes = 0;
				return BRUNCE_ERR_SYSTEM;
			}
			*wbuf = new_wbuf;
			wsize = wsize + 2*(used + ulen + vlen + 3*uint_len );
		}
                // ulen + user
		bcopy(&ulen, *wbuf + used, uint_len);     
		used += uint_len;                         
		bcopy(keyit, *wbuf + used, ulen);     
		used += ulen;
                // vlen + value
		bcopy(&vlen, *wbuf + used, uint_len);     
		used += uint_len;                         
		bcopy(f1->value.c_str(), *wbuf + used, vlen);     
		used += vlen;
                // type
                int itp = (int)(f1->type);
		bcopy(&itp, *wbuf + used, uint_len);     
		used += uint_len;
                /* sub-friends counts  */
                int isubc =  g_hash_table_size(f1->frs);
		bcopy(&isubc, *wbuf + used, uint_len);     
		used += uint_len;

		GHashTableIter iter2;
		gpointer keyit2, valueit2;
		g_hash_table_iter_init(&iter2, f1->frs);
		while (g_hash_table_iter_next(&iter2, &keyit2, &valueit2))
		{
			ulen = strlen((char *)keyit2);
			FGrid *f2 = (FGrid *)g_hash_table_lookup(m_TB, keyit2);
			int vlen = f2->value.length();    
			if(wsize < (used + ulen+ vlen + 3*uint_len ))
			{
				DEBUG1("realloc(*wbuf, wbytes+1)");
				char *new_wbuf = (char *)g_realloc(*wbuf, wsize + 2*(used + ulen + vlen + 3*uint_len));
				if(new_wbuf == NULL){
					ERRO<<"can't malloc for write buffer !"<<endl;
					wbytes = 0;
					return BRUNCE_ERR_SYSTEM;
				}
				*wbuf = new_wbuf;
				wsize = wsize + 2*(used + ulen + vlen + 3*uint_len );
			}
			// ulen + user
			bcopy(&ulen, *wbuf + used, uint_len);     
			used += uint_len;                         
			bcopy(keyit2, *wbuf + used, ulen);     
			used += ulen;
			// vlen + value
			bcopy(&vlen, *wbuf + used, uint_len);     
			used += uint_len;                         
			bcopy(f2->value.c_str(), *wbuf + used, vlen);     
			used += vlen;
			// type
			int itp = (int)(f2->type);
			bcopy(&itp, *wbuf + used, uint_len);     
			used += uint_len;
                        if(used >= MAX_WRITE_BUFFER_LEN)
                            break;
                        if(icounts >= m_iReturnMaxCounts)
                            break;
		}
	}
	wbytes = used;
	iret = BRUNCE_SUCCESS;
	DEBUG4("wbytes=", wbytes, "; iret = ", iret);
	bcopy(&wbytes, *wbuf, uint_len);
	bcopy(&iret, *wbuf + uint_len, uint_len);
	bcopy(&icounts, *wbuf + uint_len*2, uint_len);
	return iret;
}

